package com.sign.demo.filter;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

/**
 * @description 签名拦截器
 **/
@Slf4j
public class SignatureInterceptor implements HandlerInterceptor {

    @Value("${sign.secret:ZbOpzHOhsMMqfxcFoxur6s4S3sa1oPHH}")
    private String secret;

    @Value("${sign.isCheckSign:false}")
    private boolean isCheckSign;

    // 签名超时时长，默认时间为5分钟，ms
    private static final int SIGN_EXPIRED_TIME = 5 * 60 * 1000;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!isCheckSign) {
            return true;
        }
        log.info("[preHandle] executing... request uri is {}", request.getRequestURI());
        if (isJson(request)) {
            // 获取json字符串
            String jsonParam = new RequestWrapper(request).getBodyString();
            log.info("[preHandle] json数据 : {}", jsonParam);
            Map<String, Object> paramValues = this.getAttribute(jsonParam);
            log.info("requestBody={}", paramValues);
            if (paramValues == null || paramValues.size() == 0) {
                this.setUnauthorizedResponse(response, "请求参数无效");
                return false;
            }
            if (!this.checkSign(paramValues, secret)) {
                this.setUnauthorizedResponse(response, "签名失效");
                return false;
            }
            // 验签逻辑...略...
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

    /**
     * 判断本次请求的数据类型是否为json
     *
     * @param request request
     * @return boolean
     */
    private boolean isJson(HttpServletRequest request) {
        if (request.getContentType() != null) {
            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||
                    request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);
        }
        return false;
    }

    private Map<String, Object> getAttribute(String wholeStr) {
        Map<String, Object> params = new HashMap<String, Object>();
        if (StringUtils.isNotEmpty(wholeStr)) {
            params = JSON.parseObject(wholeStr, Map.class);
        }
        return params;
    }

    private void setUnauthorizedResponse(HttpServletResponse response, String msg) {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        PrintWriter out = null;
        try {
            JSONObject res = new JSONObject();
            res.put("code", "1003");
            res.put("msg", msg);
            out = response.getWriter();
            out.append(res.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean checkSign(Map<String, Object> paramValues, String secret) {
        String sign = (String) paramValues.remove("sign");
        Long time = (Long) paramValues.get("timestamp");
        Long nonce=(Long) paramValues.get("nonce");
        StringBuffer sbkey = new StringBuffer();
        sbkey.append(secret);
        if (sign == null || "".equals(sign)) {
            return false;
        }
        if (Objects.isNull(nonce)) {
            return false;
        }
        if (Objects.isNull(time) || System.currentTimeMillis() - time > SIGN_EXPIRED_TIME) {
            log.info("time is overtime, time:" + System.currentTimeMillis());
            return false;
        }
        TreeMap<String, Object> resultMap = new TreeMap<String, Object>(new MapKeyComparator());
        resultMap.putAll(paramValues);
        for (Map.Entry<String, Object> map : resultMap.entrySet()) {
            sbkey.append(map.getKey());
            sbkey.append(map.getValue());
        }
        sbkey.append(secret);
        log.info("sbkey:" + sbkey);
        String md5a = DigestUtils.md5Hex(sbkey.toString()).toUpperCase();
        log.info("md5a:" + md5a);
        if (!sign.equalsIgnoreCase(md5a)) {//验签校验
            return false;
        }
        return true;
    }

    class MapKeyComparator implements Comparator<String> {
        @Override
        public int compare(String str1, String str2) {
            return str1.compareTo(str2);
        }
    }

}
